package cn.haoran.rest;

import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;

import java.io.File;
import java.util.*;

/**
 * Create by haoRan on 2019/7/18
 */
@Slf4j
public class HttpUtils {
    private static OkHttpClient client = null;

    private HttpUtils() {}

    /**
     * 单例
     * @return
     */
    public static OkHttpClient getInstance() {
        if (client == null) {
            synchronized (HttpUtils.class) {
                if (client == null)
                    client = new OkHttpClient();
            }
        }
        return client;
    }

    /**
     * 单例（请求带cookie）
     * @return
     */
    public static OkHttpClient getInstanceWithCookie() {
        if (client == null) {
            synchronized (HttpUtils.class) {
                if (client == null)
                    client = createInstanceWithCookie();
            }
        }
        return client;
    }

    /**
     * 单例（包含cookie)
     * @return
     */
    public static OkHttpClient createInstanceWithCookie() {
        final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .cookieJar(new CookieJar() {
                    @Override
                    public void saveFromResponse(HttpUrl httpUrl, List<Cookie> list) {
                        cookieStore.put(httpUrl.host(), list);
                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl httpUrl) {
                        List<Cookie> cookies = cookieStore.get(httpUrl.host());
                        return cookies != null ? cookies : new ArrayList<Cookie>();
                    }
                })
                .build();
        return okHttpClient;
    }

    /**
     * 根据map获取get请求参数
     * @param queries
     * @return
     */
    public static StringBuffer getQueryString(String url,Map<String,String> queries){
        StringBuffer sb = new StringBuffer(url);
        if (queries != null && queries.keySet().size() > 0) {
            boolean firstFlag = true;
            Iterator iterator = queries.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry entry = (Map.Entry<String, String>) iterator.next();
                if (firstFlag) {
                    sb.append("?" + entry.getKey() + "=" + entry.getValue());
                    firstFlag = false;
                } else {
                    sb.append("&" + entry.getKey() + "=" + entry.getValue());
                }
            }
        }
        return sb;
    }

    /**
     * get
     * @param url     请求的url
     * @param queries 请求的参数，在浏览器？后面的数据，没有可以传null
     * @return
     */
    public static String get(String url, Map<String, String> queries) {
        StringBuffer sb = getQueryString(url,queries);
        Request request = new Request.Builder()
                .url(sb.toString())
                .build();
        return call(request);
    }

    /**
     * get请求（带cookie）
     * @param url
     * @param queries
     * @return
     */
    public static String getWithCookie(String url, Map<String, String> queries) {
        StringBuffer sb = getQueryString(url,queries);
        Request request = new Request.Builder()
                .url(sb.toString())
                .build();
        return callWithCookie(request);
    }

    /**
     * post
     *
     * @param url    请求的url
     * @param params post form 提交的参数
     * @return
     */
    public static String postFormParams(String url, Map<String, String> params) {
        FormBody.Builder builder = new FormBody.Builder();
        //添加参数
        if (params != null && params.keySet().size() > 0) {
            for (String key : params.keySet()) {
                builder.add(key, params.get(key));
            }
        }
        Request request = new Request.Builder()
                .url(url)
                .post(builder.build())
                .build();
        return call(request);
    }


    /**
     * Post请求发送JSON数据....{"name":"zhangsan","pwd":"123456"}
     * 参数一：请求Url
     * 参数二：请求的JSON
     */
    public static String postJsonParams(String url, String jsonParams) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonParams);
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        return call(request);
    }

    /**
     * post请求，参数json格式，header0带User-Agent
     * @param url 请求url
     * @param jsonParams json格式参数
     * @param userAgent 请求头
     * @return
     */
    public static String postJsonParams(String url, String jsonParams, String userAgent) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonParams);
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("User-Agent", userAgent)
                .build();
        return call(request);
    }

    /**
     * post请求(带cookie)，参数json格式，header带User-Agent
     * @param url 请求url
     * @param jsonParams json格式参数
     * @param userAgent 请求头
     * @return
     */
    public static String postJsonParamsWithCookie(String url, String jsonParams, String userAgent) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonParams);
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("User-Agent", userAgent)
                .build();
        return callWithCookie(request);
    }

    /**
     * post请求（带cookie），文件上传，header带User-Agent
     * @param url
     * @param file
     * @param userAgent
     * @return
     */
    public static String postFileParamsWithCookie(String url, String fileName, File file, String userAgent) {
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM) // multipart/form-data
                //.addFormDataPart("title","测试图片") 这里可以同时传其他表单参数
                .addFormDataPart("file",fileName, RequestBody.create(MediaType.parse("image/png"), file))
                .build();
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("User-Agent", userAgent)
                .build();
        return callWithCookie(request);
    }

    /**
     * put请求（带cookie），参数json格式， header带User-Agent
     * @param url 请求url
     * @param jsonParams json格式参数
     * @param userAgent 请求头
     * @return
     */
    public static String putJsonParamsWithCookie(String url, String jsonParams, String userAgent) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonParams);
        Request request = new Request.Builder()
                .url(url)
                .put(requestBody)
                .addHeader("User-Agent", userAgent)
                .build();
        return callWithCookie(request);
    }

    /**
     * delete请求（带cookie），参数json格式， header带User-Agent
     * @param url 请求url
     * @param params map格式参数
     * @param userAgent 请求头
     * @return
     */
    public static String deleteParamsWithCookie(String url, Map<String, String> params,
                                                    String userAgent) {
        MultipartBody.Builder builder = new MultipartBody.Builder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            builder = builder.addFormDataPart(entry.getKey(), entry.getValue());
        }
        RequestBody requestBody = builder
                .setType(MultipartBody.FORM) // multipart/form-data
                //.addFormDataPart("title","测试图片") //这里可以同时传其他表单参数
                .build();
        Request request = new Request.Builder()
                .url(url)
                .delete(requestBody)
                .addHeader("User-Agent", userAgent)
                //.addHeader("Content-Type", "multipart/form-data")
                .build();
        return callWithCookie(request);
    }

    /**
     * delete请求（带cookie），header带User-Agent
     * @param url 请求url
     * @param userAgent 请求头
     * @param contentType 接口格式
     * @return
     */
    public static String deleteWithCookie(String url, String userAgent, String contentType) {
        if(StringUtils.isBlank(contentType)) {
            contentType = "multipart/form-data; charset=utf-8";
        }
        // RequestBody requestBody = RequestBody.create(MediaType.parse(contentType), "");
        Request request = new Request.Builder()
                .url(url)
                .delete()
                .addHeader("User-Agent", userAgent)
                .addHeader("Content-Type", contentType)
                .build();
        return callWithCookie(request);
    }


    /**
     * Post请求发送xml数据....
     * 参数一：请求Url
     * 参数二：请求的xmlString
     * 参数三：请求回调
     */
    public static String postXmlParams(String url, String xml) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/xml; charset=utf-8"), xml);
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        return call(request);
    }

    /**
     * 调用okhttp的newCall方法
     * @param request
     * @return
     */
    private static String call(Request request){
        Response response = null;
        try {
            OkHttpClient okHttpClient = getInstance();
            response = okHttpClient.newCall(request).execute();
            int status = response.code();
            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (Exception e) {
            log.error("okhttp3 put error >> ex = {}", ExceptionUtils.getStackTrace(e));
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return "";
    }

    private static String callWithCookie(Request request) {
        Response response = null;
        try {
            OkHttpClient okHttpClient = getInstanceWithCookie();
            response = okHttpClient.newCall(request).execute();
            int status = response.code();
            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (Exception e) {
            log.error("okhttp3 put error >> ex = {}", ExceptionUtils.getStackTrace(e));
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return "";
    }
}
